/*
 * Copyright (c) 2015 by Greg Reimer <gregreimer@gmail.com>
 * MIT License. See mit-license.txt for more info.
 */
import pem, {
	CertificateCreationOptions,
	CertificateCreationResult,
} from "pem";
import tls from "tls";
import { EventEmitter } from "events";

export class SNISpoofer extends EventEmitter {
	_rootKey: string;
	_rootCert: string;
	_cache: Map<any, any>;

	constructor(rootKey: string, rootCert: string) {
		super();
		this._rootKey = rootKey;
		this._rootCert = rootCert;
		this._cache = new Map();
	}

	callback() {
		let cache = this._cache,
			rootKey = this._rootKey,
			rootCert = this._rootCert,
			_this = this;

		let getCachedCtx = async function (serverName: string) {
			let ctx = cache.get(serverName);
			if (!ctx) {
				ctx = await getCtx(serverName);
				cache.set(serverName, ctx);
			}
			return ctx;
		};

		let getCtx = async function (serverName: string) {
			let create = (ops: CertificateCreationOptions) => {
				return new Promise<CertificateCreationResult>(
					(resolve, reject) => {
						pem.createCertificate(ops, (err, keys) => {
							if (err) {
								reject(err);
							} else {
								resolve(keys);
							}
						});
					}
				);
			};

			let { clientKey: key, certificate: cert } = await create({
				country: "US",
				state: "Utah",
				locality: "Provo",
				organization: "ACME Tech Inc",
				commonName: serverName,
				altNames: [serverName],
				serviceKey: rootKey,
				serviceCertificate: rootCert,
				serial: Date.now(),
				days: 500,
			});
			let ctx = tls.createSecureContext({ key, cert });
			_this.emit("generate", serverName);
			return ctx;
		};

		return (serverName: string, cb: (...args: any[]) => void) => {
			getCachedCtx(serverName).then(
				(ctx) => cb(null, ctx),
				(err) => {
					_this.emit("error", err);
					cb(err);
				}
			);
		};
	}
}
